# -*- coding: utf-8 -*-

"""
京东配送API SDK主要实现
"""

import json
import time
import hashlib
import logging
from datetime import datetime
from typing import Dict, Any, Optional, Union

import requests

from .models import ApiConfig, ApiResult, OrderData, MerchantInfo


class JingdongApiSDK:
    """
    京东配送API SDK
    
    用于调用京东配送API的各种服务，包括订单创建、查询、取消等功能
    """
    
    # 版本号
    VERSION = '1.0.0'
    
    # API地址配置
    API_BASE_TEST = 'https://api-test.hiboos.com'
    API_BASE_SANDBOX = 'https://api-sandbox.hiboos.com'
    API_BASE_PROD = 'https://api.hiboos.com'
    
    def __init__(self, app_key: str, app_secret: str, merchant_no: str,
                 sandbox_mode: bool = False, timeout: int = 30,
                 enable_log: bool = True, user_agent: Optional[str] = None):
        """
        初始化SDK
        
        Args:
            app_key: 应用密钥
            app_secret: 应用密码
            merchant_no: 商户号
            sandbox_mode: 是否沙箱模式
            timeout: 请求超时时间（秒）
            enable_log: 是否启用日志
            user_agent: 自定义User-Agent
        """
        self.config = ApiConfig(
            app_key=app_key,
            app_secret=app_secret,
            merchant_no=merchant_no,
            sandbox_mode=sandbox_mode,
            timeout=timeout,
            enable_log=enable_log,
            user_agent=user_agent
        )
        
        # 初始化日志
        self.logger = self._init_logger()
        
        # 初始化HTTP会话
        self.session = requests.Session()
        self.session.headers.update({
            'User-Agent': self.config.user_agent,
            'Content-Type': 'application/x-www-form-urlencoded'
        })
        
        self.logger.info(f"京东配送API SDK初始化完成 - 版本: {self.VERSION}, "
                        f"环境: {'沙箱' if sandbox_mode else '生产'}")
    
    def _init_logger(self) -> logging.Logger:
        """初始化日志"""
        logger = logging.getLogger('jingdong_api_sdk')
        
        if not logger.handlers and self.config.enable_log:
            handler = logging.StreamHandler()
            formatter = logging.Formatter(
                '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
            )
            handler.setFormatter(formatter)
            logger.addHandler(handler)
            logger.setLevel(logging.INFO)
        
        return logger
    
    def _generate_sign(self, params: Dict[str, Any]) -> str:
        """
        生成签名
        
        Args:
            params: 请求参数
            
        Returns:
            签名字符串
        """
        # 移除签名参数
        params.pop('sign', None)
        
        # 按key排序
        sorted_params = sorted(params.items())
        
        # 拼接参数字符串
        string_to_be_signed = self.config.app_secret
        for key, value in sorted_params:
            if value is not None and str(value).strip():
                string_to_be_signed += key + str(value)
        string_to_be_signed += self.config.app_secret
        
        # MD5加密并转大写
        return hashlib.md5(string_to_be_signed.encode('utf-8')).hexdigest().upper()
    
    def _request(self, method: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        """
        发送HTTP请求
        
        Args:
            method: API方法名
            params: 请求参数
            
        Returns:
            响应数据
            
        Raises:
            Exception: 请求失败时抛出
        """
        url = self._get_base_url() + '/api/' + method
        
        # 公共参数
        common_params = {
            'app_key': self.config.app_key,
            'method': method,
            'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
            'format': 'json',
            'v': '1.0',
            'sign_method': 'md5'
        }
        
        # 合并参数
        all_params = common_params.copy()
        if params:
            all_params.update(params)
        
        # 生成签名
        all_params['sign'] = self._generate_sign(all_params)
        
        try:
            response = self.session.post(
                url,
                data=all_params,
                timeout=self.config.timeout
            )
            
            response.raise_for_status()
            
            result = response.json()
            
            # 记录API调用日志
            if self.config.enable_log:
                self.logger.info(f"JingdongAPI Request - Method: {method}, "
                               f"Params: {all_params}, Response: {result}")
            
            return result
            
        except Exception as e:
            self.logger.error(f"JingdongAPI Error - Method: {method}, "
                            f"Params: {all_params}, Error: {str(e)}")
            raise e
    
    def _get_base_url(self) -> str:
        """获取基础URL"""
        if self.config.sandbox_mode:
            return self.API_BASE_SANDBOX
        else:
            return self.API_BASE_PROD
    
    def _convert_coordinate(self, coordinate: float) -> int:
        """
        转换坐标格式
        将普通坐标转换为火星坐标*10^6格式
        """
        return int(coordinate * 1000000)
    
    def _convert_weight(self, weight: float) -> int:
        """
        转换重量格式
        将重量转换为克(g)
        """
        return int(weight * 1000)
    
    def apply_merchant(self, merchant_info: Union[MerchantInfo, Dict[str, str]]) -> ApiResult:
        """
        1. 申请成为配送商
        
        Args:
            merchant_info: 商户信息
            
        Returns:
            API结果
        """
        if isinstance(merchant_info, dict):
            merchant_info = MerchantInfo(**merchant_info)
        
        try:
            response = self._request('jingdong.logistics.merchant.apply', 
                                   merchant_info.to_dict())
            return ApiResult.from_response(response)
        except Exception as e:
            return ApiResult.error_result(-1, str(e))
    
    def query_merchant_status(self, apply_id: Optional[str] = None) -> ApiResult:
        """
        2. 查询配送商审核状态
        
        Args:
            apply_id: 申请ID（可选）
            
        Returns:
            API结果
        """
        try:
            params = {}
            if apply_id:
                params['apply_id'] = apply_id
            
            response = self._request('jingdong.logistics.merchant.query', params)
            return ApiResult.from_response(response)
        except Exception as e:
            return ApiResult.error_result(-1, str(e))
    
    def create_order(self, order_data: Union[OrderData, Dict[str, Any]]) -> ApiResult:
        """
        3. 创建配送订单（发单）
        
        Args:
            order_data: 订单数据
            
        Returns:
            API结果
        """
        if isinstance(order_data, dict):
            order_data = OrderData(**order_data)
        
        try:
            # 构建请求参数
            params = {
                # 必填参数
                'tradeOrderSource': order_data.trade_order_source,
                'orderId': order_data.order_id,
                'serviceCode': order_data.service_code,
                'recipientName': order_data.recipient_name,
                'recipientPhone': order_data.recipient_phone,
                'recipientAddress': order_data.recipient_address,
                'recipientLng': self._convert_coordinate(order_data.recipient_lng),
                'recipientLat': self._convert_coordinate(order_data.recipient_lat),
                'prebook': order_data.prebook,
                'totalWeight': self._convert_weight(order_data.total_weight),
                'senderLng': self._convert_coordinate(order_data.sender_lng),
                'senderLat': self._convert_coordinate(order_data.sender_lat),
                'senderName': order_data.sender_name,
                'senderContract': order_data.sender_phone,
                'senderAddressDetail': order_data.sender_address,
                'operatorName': order_data.operator_name or order_data.sender_name,
                'carrierMerchantId': self.config.merchant_no,
                'extInfo': json.dumps(order_data.ext_info, ensure_ascii=False)
            }
            
            # 添加可选参数
            optional_fields = [
                ('tradeOrderId', 'trade_order_id'),
                ('orderSequence', 'order_sequence'),
                ('preDeliveryFee', 'pre_delivery_fee'),
                ('expectedDeliveryTime', 'expected_delivery_time'),
                ('expectedPickupTime', 'expected_pickup_time'),
                ('expectedLeftDeliveryTime', 'expected_left_delivery_time'),
                ('expectedRightDeliveryTime', 'expected_right_delivery_time'),
                ('insuredMark', 'insured_mark'),
                ('totalValue', 'total_value'),
                ('totalVolume', 'total_volume'),
                ('riderPickMethod', 'rider_pick_method'),
                ('tipFee', 'tip_fee'),
                ('orderRemark', 'order_remark'),
                ('shopRemark', 'shop_remark'),
                ('carrierShopId', 'carrier_shop_id'),
                ('carModelCode', 'car_model_code'),
                ('riderInfo', 'rider_info'),
                ('category', 'category')
            ]
            
            for param_name, attr_name in optional_fields:
                value = getattr(order_data, attr_name)
                if value is not None:
                    if param_name in ['expectedDeliveryTime', 'expectedPickupTime', 
                                    'expectedLeftDeliveryTime', 'expectedRightDeliveryTime']:
                        # 时间戳转换
                        if isinstance(value, str):
                            value = int(time.mktime(time.strptime(value, '%Y-%m-%d %H:%M:%S')))
                        params[param_name] = value
                    else:
                        params[param_name] = value
            
            # 处理操作员电话
            if order_data.operator_phone:
                params['operatorContract'] = order_data.operator_phone
            
            # 处理物品明细
            if order_data.goods_details:
                params['goodsDetails'] = json.dumps(order_data.goods_details, ensure_ascii=False)
            
            response = self._request('hiboos.order.create', params)
            return ApiResult.from_response(response)
        except Exception as e:
            return ApiResult.error_result(-1, str(e))
    
    def query_order(self, out_order_id: Optional[str] = None, 
                   jd_order_id: Optional[str] = None) -> ApiResult:
        """
        4. 查询订单状态
        
        Args:
            out_order_id: 外部订单号
            jd_order_id: 京东订单号
            
        Returns:
            API结果
        """
        if not out_order_id and not jd_order_id:
            return ApiResult.error_result(-1, "外部订单号和京东订单号至少提供一个")
        
        try:
            params = {}
            if out_order_id:
                params['out_order_id'] = out_order_id
            if jd_order_id:
                params['jd_order_id'] = jd_order_id
            
            response = self._request('jingdong.logistics.order.query', params)
            return ApiResult.from_response(response)
        except Exception as e:
            return ApiResult.error_result(-1, str(e))
    
    def cancel_order(self, out_order_id: str, cancel_reason: str = "用户取消") -> ApiResult:
        """
        5. 取消订单
        
        Args:
            out_order_id: 外部订单号
            cancel_reason: 取消原因
            
        Returns:
            API结果
        """
        try:
            params = {
                'out_order_id': out_order_id,
                'cancel_reason': cancel_reason
            }
            
            response = self._request('jingdong.logistics.order.cancel', params)
            return ApiResult.from_response(response)
        except Exception as e:
            return ApiResult.error_result(-1, str(e))
    
    def query_rider_location(self, out_order_id: str) -> ApiResult:
        """
        6. 骑手位置查询
        
        Args:
            out_order_id: 外部订单号
            
        Returns:
            API结果
        """
        try:
            params = {
                'out_order_id': out_order_id
            }
            
            response = self._request('jingdong.logistics.rider.location', params)
            return ApiResult.from_response(response)
        except Exception as e:
            return ApiResult.error_result(-1, str(e))
    
    def pre_order(self, order_data: Dict[str, Any]) -> ApiResult:
        """
        7. 预下单接口（获取运费）
        
        Args:
            order_data: 订单数据
            
        Returns:
            API结果
        """
        try:
            params = {
                'cargo_type': order_data.get('cargo_type', '1'),
                'cargo_weight': order_data['cargo_weight'],
                'cargo_num': order_data.get('cargo_num', '1'),
                'cargo_price': order_data['cargo_price'],
                'pickup_info': json.dumps(order_data['pickup_info'], ensure_ascii=False),
                'delivery_info': json.dumps(order_data['delivery_info'], ensure_ascii=False),
                'expected_pickup_time': order_data['expected_pickup_time'],
                'expected_delivery_time': order_data['expected_delivery_time']
            }
            
            response = self._request('jingdong.logistics.order.preorder', params)
            return ApiResult.from_response(response)
        except Exception as e:
            return ApiResult.error_result(-1, str(e))
    
    def query_service_area(self, lng: str, lat: str) -> ApiResult:
        """
        8. 查询可配送范围
        
        Args:
            lng: 经度
            lat: 纬度
            
        Returns:
            API结果
        """
        try:
            params = {
                'longitude': lng,
                'latitude': lat
            }
            
            response = self._request('jingdong.logistics.area.query', params)
            return ApiResult.from_response(response)
        except Exception as e:
            return ApiResult.error_result(-1, str(e))
    
    def query_order_list(self, condition: Optional[Dict[str, Any]] = None) -> ApiResult:
        """
        9. 查询历史订单列表
        
        Args:
            condition: 查询条件
            
        Returns:
            API结果
        """
        if condition is None:
            condition = {}
        
        try:
            params = {
                'page_size': condition.get('page_size', 20),
                'page_no': condition.get('page_no', 1)
            }
            
            # 可选参数
            optional_fields = ['start_time', 'end_time', 'order_status']
            for field in optional_fields:
                if field in condition:
                    params[field] = condition[field]
            
            response = self._request('jingdong.logistics.order.list', params)
            return ApiResult.from_response(response)
        except Exception as e:
            return ApiResult.error_result(-1, str(e))
    
    def add_tips(self, out_order_id: str, tips: str) -> ApiResult:
        """
        10. 订单追加小费
        
        Args:
            out_order_id: 外部订单号
            tips: 小费金额
            
        Returns:
            API结果
        """
        try:
            params = {
                'out_order_id': out_order_id,
                'tips': tips
            }
            
            response = self._request('jingdong.logistics.order.addtips', params)
            return ApiResult.from_response(response)
        except Exception as e:
            return ApiResult.error_result(-1, str(e))
    
    def urge_order(self, out_order_id: str) -> ApiResult:
        """
        11. 订单催单
        
        Args:
            out_order_id: 外部订单号
            
        Returns:
            API结果
        """
        try:
            params = {
                'out_order_id': out_order_id
            }
            
            response = self._request('jingdong.logistics.order.urge', params)
            return ApiResult.from_response(response)
        except Exception as e:
            return ApiResult.error_result(-1, str(e))
    
    def get_order_detail(self, out_order_id: str) -> ApiResult:
        """
        12. 获取订单详情
        
        Args:
            out_order_id: 外部订单号
            
        Returns:
            API结果
        """
        try:
            params = {
                'out_order_id': out_order_id
            }
            
            response = self._request('jingdong.logistics.order.detail', params)
            return ApiResult.from_response(response)
        except Exception as e:
            return ApiResult.error_result(-1, str(e))
    
    def verify_callback(self, data: Dict[str, Any], signature: str) -> bool:
        """
        13. 验证回调签名
        
        Args:
            data: 回调数据
            signature: 签名
            
        Returns:
            验证结果
        """
        local_sign = self._generate_sign(data.copy())
        return local_sign == signature
    
    def get_config(self) -> Dict[str, Any]:
        """
        获取配置信息
        
        Returns:
            配置信息
        """
        return {
            'app_key': self.config.app_key,
            'merchant_no': self.config.merchant_no,
            'sandbox_mode': self.config.sandbox_mode,
            'base_url': self._get_base_url()
        }
    
    def test_connection(self) -> ApiResult:
        """
        测试API连接
        
        Returns:
            API结果
        """
        try:
            result = self.query_merchant_status()
            if result.success:
                return ApiResult.success_result(result.data, "API连接正常")
            else:
                return result
        except Exception as e:
            return ApiResult.error_result(-1, f"API连接失败: {str(e)}")
    
    def close(self):
        """关闭资源"""
        if hasattr(self, 'session'):
            self.session.close()
        
        self.logger.info("京东配送API SDK资源已释放")
